import numpy as np
import math
import PIL.Image as Image
from gauseKernel import gauseKernel, Conv2D

class Conv2D_SIFT(Conv2D):
    def __init__(self, filters, kernel_size, sigma, boundary="zero", padding="same"):
        super().__init__(filters, kernel_size, sigma, boundary=boundary, padding=padding)
    def changeFilters(self, filters):
        self.filters = filters

class SIFT():
    def __init__(self, sigma, k):
        self.sigma = sigma
        self.k = k
        self.interestPoints = []
        self.descriptors = []
        self.detectors = []
        for i in range(5):
            tmp = gauseKernel(3, pow(k, i + 1) * sigma) - gauseKernel(3, pow(k, i) * sigma)
            print(tmp)
            self.detectors.append(tmp)
        self.conv2D = Conv2D_SIFT(3, 3, 1)
        self.conv2D.changeFilters(self.detectors)
    
    def clearLast(self):
        self.interestPoints = []
        self.descriptors = []
        self.conv2D = Conv2D_SIFT(3, 3, 1)
        self.conv2D.changeFilters(self.detectors)

    def isInterest(self, outputs, i, j, h, w, kth):
        for m in range(-1, 2):
            for n in range(-1, 2):
                if i + m >= 0 and i + m < h and j + n >=0 and j + n < w:
                    if outputs[kth][i][j] <= outputs[kth-1][i + m][j + n] or outputs[kth][i][j] <= outputs[kth+1][i + m][j + n] or (outputs[kth][i][j] <= outputs[kth][i + m][j + n] and m != 0 and n != 0):
                        return False
        return True

    def call(self, filepath):
        self.clearLast()
        image = Image.open(filepath)
        imageL = image.convert("L")
        inputs = np.array(imageL)
        outputs = self.conv2D(inputs)
        print(len(outputs))
        h = inputs.shape[0]
        w = inputs.shape[1]
        for i in range(h):
            for j in range(w):
                if self.isInterest(outputs, i, j, h, w, 1):
                    self.interestPoints.append((i, j))
                if self.isInterest(outputs, i, j, h, w, 2):
                    if (i,j) not in self.interestPoints:
                        self.interestPoints.append((i, j))
                if self.isInterest(outputs, i, j, h, w, 3):
                    if (i,j) not in self.interestPoints:
                        self.interestPoints.append((i, j))
        print("total interested points num: ", len(self.interestPoints))
        # self.printInterested(inputs)
        result = np.zeros_like(inputs)
        for item in self.interestPoints:
            result[item[0]][item[1]] = 255
        resultImage = Image.fromarray(result.astype("uint")).convert("L")
        resultImage.save("./SIFT/interestedPoints_rawImage_rotate.jpg")
        return outputs, self.interestPoints

    def printInterested(self, inputs):
        for point in self.interestPoints:
            print(inputs[point[0]][point[1]])
    
    # def get

    def __call__(self, filepath):
        return self.call(filepath)

if __name__ == "__main__":
    sift = SIFT(1.6, 1.26)
    outputs, interestedPoints = sift("./SIFT/euclideanbackwardcat-0-0-0.5235987755982988.jpg")
    print(interestedPoints)


